package dkbta.ontology.elements.abstractions.gradient;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

import dkbta.ontology.Ontology;
import dkbta.ontology.elements.ElementType;
import dkbta.ontology.elements.abstractions.AbstractionDef;
import dkbta.ontology.elements.abstractions.TemporalSemanticProperty;
import dkbta.ontology.elements.abstractions.interpolation.PersistenceFunction;
import dkbta.ontology.elements.primitive.PrimitiveDef;
import dkbta.ontology.loading.OntologyException;
import dkbta.ontology.values.ScaledSymbolicValues;
import dkbta.ontology.values.ValuesType;
import dkbta.processing.ActionTimer;
import dkbta.processing.Patient;

public class GradientDef extends AbstractionDef{
	private final GradientMappingFunction _mappingFunction;

	/** The primitive the gradient is abstracted from */
	private final String _abstractedFrom;

	/**
	 * Initializes an ontology element with the given name and component type
	 * 
	 * @param name The element's name
	 * @param componentType The element's component type
	 * @param possibleValues The abstraction's possible values
	 * @param abstractedFrom The name of the primitive the gradient is abstracted from
	 * @param necessaryContexts The contexts needed for the abstraction
	 * @param tempSemProperties The temporal semantics properties of this abstraction
	 * @param mapFunc The gradient's mapping function
	 * @param persistFunc The gradient's persistence function
	 */
	public GradientDef(String name, String componentType,
		ScaledSymbolicValues possibleValues, String abstractedFrom,
		Set<String> necessaryContexts, Set<TemporalSemanticProperty> tempSemProperties,
		GradientMappingFunction mapFunc, PersistenceFunction persistFunc){
		super(ElementType.GRADIENT, name, componentType, possibleValues,
				necessaryContexts, tempSemProperties, persistFunc);
		_abstractedFrom = abstractedFrom;
		_mappingFunction = mapFunc;
	}

	@Override
	public void validate(Ontology ontology) throws OntologyException{
		super.validate(ontology);
		PrimitiveDef pd = ontology.getPrimitive(_abstractedFrom);
		if (pd == null){
			throw new OntologyException(
					"The gradient if abstracted from a non-existing primitive: "
							+ _abstractedFrom);
		}
		ValuesType valueType = pd.getValueType();
		if (valueType != ValuesType.NUMERIC){
			throw new OntologyException(
					"The gradient must abstracted from a numeric primitive. "
							+ _abstractedFrom + " is " + valueType);
		}
		_prerequisiteElements.add(pd);
	}

	@Override
	public String completeToString(){
		StringBuilder sb = new StringBuilder(super.completeToString());
		sb.append("\r\nAbstracted From: ").append(_abstractedFrom);
		sb.append("\r\nMapping Function: ").append(_mappingFunction);
		sb.append("\r\n");
		return sb.toString();
	}

	@Override
	public Gradient newInstance(long startTime, long endTime, String[] value){
		Object validatedValue;
		if (value.length == 0
				|| (validatedValue = getValues().parseAndValidate(value[0])) == null){
			throw new IllegalArgumentException("Invalid value: " + Arrays.toString(value));
		}
		return new Gradient(_name, startTime, endTime, validatedValue);
	}

	@Override
	protected void compute(Patient p){
		List<Gradient> gradients = p.getGradients(_name);

		ActionTimer actionTimer = ActionTimer.getInstance();

		// Performing the mapping / abstraction
		int mappingActionId = actionTimer.actionStart();
		_mappingFunction.performMapping(p, gradients, _name, _abstractedFrom,
			_necessaryContexts, _interpolation);
		actionTimer.actionEnd(mappingActionId, p.getPatientId(), _name, _type,
			ActionTimer.Action.MAPPING);

		// Performing the interpolation
		int interpolationActionId = actionTimer.actionStart();
		performInterpolation(gradients);
		actionTimer.actionEnd(interpolationActionId, p.getPatientId(), _name, _type,
			ActionTimer.Action.INTERPOLATION);
	}
}
